/* 
 * File:   Factory.h
 * Author: RedEyedKiller
 *
 * Created on 24 Απρίλιος 2010, 11:52 μμ
 */

#ifndef _FACTORY_H
#define	_FACTORY_H

#include <string>
#include <map>

// define DEBUG symbol if you want to check keys for existance and output errors in
// std error.
//#define DEBUG

#ifdef DEBUG
#include <iostream>
#endif

/* This is the template method used for CreationFunction
 * each instance of Factory will need a specialization of it.
 */
template<typename BaseClass, typename ConcreteClass>
BaseClass* CreateObject()
{
    return (BaseClass*)(new ConcreteClass);
}

/* This is a example of typedef for the factory
 * The return type of this definition must be the same as
 * the specialization of CreateObject template class.
 * ex. typedef Entity* (*CreateEntity)();
 */

/* This is a generic factory class.
 * it can be used to simplify the prcess of object allocation.
 *
 * ex Factory<Entity,CreateEntity> factory;
 *    Entity* = factory.Create("Zombie1");
 */

/** Product the abstract interface
 * CreationFunction the typedefed function which returns Product*
 * KeyType the type of data used as a key in the internal map
 */
template <typename KeyType,typename Product, typename CreationFunction>
class Factory
{
public:

    Factory(){}

    virtual ~Factory()
    {
        binding.clear();
    }

    /**
     * @param ConcreteClass as template - the typename of final product.
     * @param key - a value that desrcibes the ConcreateClass product
     *
     * This method is required in order to create a map of keys and the
     * corresponding products.
     * ex. Register<Actor>("zombie1");
     * after that any call to Create("Zombie1");
     * will return an Actor.
     */
    template<typename ConcreteClass>
    void Register(KeyType key)
    {
        std::pair<KeyType, CreationFunction> pair;
        pair.first = key;
        pair.second = &CreateObject <Product, ConcreteClass>;
        binding.insert(pair);
    }

    /**
     *
     * @param key - they key of the disered product
     * @return the product requested
     */
    Product* Create(KeyType key)
    {
#ifdef  DEBUG
        typename std::map<KeyType, CreationFunction>::iterator result(binding.find(key));
        if (result == binding.end())
        {
            std::cerr << "\nError : Not registered key while trying to create object in factory" << std::endl;
            return NULL;
        }
#endif
        return binding[key]();
    }
private:
    std::map<KeyType, CreationFunction> binding;
};

#endif	/* _FACTORY_H */

